#ifndef GUI_H
#define GUI_H

#include "Color.h"

#include <QtGui/qpaintdevice.h>
#include <QtGui/qpainter.h>
#include <QtGui/qimage.h>

#include <exception>

class QGLWidget;

namespace GUI
{

// A point in relative continuous coordinates
// (range 0..1 is visible in an object's associated GraphDisplay).
struct Point
{
  float x, y;

  Point operator+( const Point& p ) const
  {
    Point r = { x + p.x, y + p.y };
    return r;
  }

  Point operator-( const Point& p ) const
  {
    Point r = { x - p.x, y - p.y };
    return r;
  }

  bool operator==( const Point& p ) const
  {
    return x == p.x && y == p.y;
  }

  bool operator!=( const Point& p ) const
  {
    return !operator==( p );
  }
};

// A rectangle in continuous coordinates.
struct Rect
{
  float left, top, right, bottom;

  float Width() const
  {
    return right - left;
  }

  float Height() const
  {
    return bottom - top;
  }

  bool operator==( const Rect& r ) const
  {
    return left == r.left
        && right == r.right
        && top == r.top
        && bottom == r.bottom;
  }

  bool operator!=( const Rect& r ) const
  {
    return !operator==( r );
  }
};

// Test whether a point is contained in a rectangle.
bool PointInRect( const Point& p, const Rect& r );
bool EmptyRect( const Rect& r );

// A draw context consists of an OS-dependent GUI device handle, and a target
// rectangle in that device's coordinates.
// For Win32, the GUI device handle is a HDC (device context handle), not a
// window handle.
// If we're using Qt, we need to pass around a QPaintDevice, as that
// is the equivalent of an HDC for a QWidget (our main window), _and_
// a QPainter because this is the only way to implement clipping when drawing
// to an offscreen buffer.
struct DrawContext
{
  struct
  {
    QPaintDevice* device;
    QPainter*     painter;
    QGLWidget*    glContext;
  } handle;

  Rect  rect;
};

namespace RenderingMode
{
  enum
  {
    Transparent,
    Opaque
  };
}

namespace GraphicResource
{
  template<class T> 
  int Width( const T& t ) { return t[ 0 ].count; }

  template<class T>
  int Height( const T& t ) { return t[ 1 ].count; }

  template<int Mode, class T>
  void Render( const T&, const DrawContext& );
};

} // namespace GUI

template<int Mode, class T>
void GUI::GraphicResource::Render( const T& inGraphic, const GUI::DrawContext& inDC )
{
  if( inDC.handle.device != NULL )
  {
    if( Mode != RenderingMode::Opaque )
      throw exception( "Unsupported rendering mode: Transparent");

    int width = Width( inGraphic ),
        height = Height( inGraphic );
    QImage image( width, height, QImage::Format_RGB888 );
    int run = 2,
        x = 0,
        y = 0;
    while( inGraphic[ run ].count > 0 )
    {
      int grayValue = inGraphic[ run ].color;
      for( int i = 0; i < inGraphic[ run ].count; ++i )
      {
        image.setPixel( x, y, qRgb( grayValue, grayValue, grayValue ) );
        if( ++x >= width )
        {
          ++y;
          x = 0;
        }
      }
      ++run;
    }
    QRect targetRect(
      static_cast<int>( inDC.rect.left ),
      static_cast<int>( inDC.rect.top ),
      static_cast<int>( inDC.rect.right - inDC.rect.left ),
      static_cast<int>( inDC.rect.bottom - inDC.rect.top )
    );
    if( inDC.handle.painter != NULL )
    {
      inDC.handle.painter->drawImage( targetRect, image );
    }
    else
    {
      QPainter p( inDC.handle.device );
      p.drawImage( targetRect, image );
    }
  }
}

#endif // GUI_H
